package redlib.backend.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import redlib.backend.dao.EssayMapper;
import redlib.backend.dto.EssayDTO;
import redlib.backend.dto.query.EssayFullSearchQueryDTO;
import redlib.backend.dto.query.EssayQueryDTO;
import redlib.backend.model.Essay;
import redlib.backend.model.Page;
import redlib.backend.model.Token;
import redlib.backend.service.AdminService;
import redlib.backend.service.EssayService;
import redlib.backend.service.utils.EssayUtils;
import redlib.backend.utils.ElasticsearchUtils;
import redlib.backend.utils.FormatUtils;
import redlib.backend.utils.PageUtils;
import redlib.backend.utils.ThreadContextHolder;
import redlib.backend.vo.EssayVO;
import redlib.backend.vo.LuceneResultBookVO;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class EssayServiceImpl implements EssayService {
    @Autowired
    private EssayMapper essayMapper;

    @Autowired
    private AdminService adminService;

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    /**
     * 分页获取部门信息
     *
     * @param queryDTO 查询条件和分页信息
     * @return 带分页信息的部门数据列表
     */
    @Override
    public Page<EssayVO> listByPage(EssayQueryDTO queryDTO) {
        if (queryDTO == null) {
            queryDTO = new EssayQueryDTO();
        }

        queryDTO.setTitle(FormatUtils.makeFuzzySearchTerm(queryDTO.getTitle()));
        Integer size = essayMapper.count(queryDTO);
        PageUtils pageUtils = new PageUtils(queryDTO.getCurrent(), queryDTO.getPageSize(), size);

        if (size == 0) {
            // 没有命中，则返回空数据。
            return pageUtils.getNullPage();
        }

        // 利用myBatis到数据库中查询数据，以分页的方式
        List<Essay> list = essayMapper.list(queryDTO, pageUtils.getOffset(), pageUtils.getLimit());

        // 提取list列表中的创建人字段，到一个Set集合中去
        Set<Integer> adminIds = list.stream().map(Essay::getCreatedBy).collect(Collectors.toSet());

        // 提取list列表中的更新人字段，追加到集合中去
        adminIds.addAll(list.stream().map(Essay::getCreatedBy).collect(Collectors.toSet()));

        // 获取id到人名的映射
        Map<Integer, String> nameMap = adminService.getNameMap(adminIds);

        List<EssayVO> voList = new ArrayList<>();
        for (Essay essay : list) {
            // Essay对象转VO对象
            EssayVO vo = EssayUtils.convertToVO(essay, nameMap);
            voList.add(vo);
        }

        return new Page<>(pageUtils.getCurrent(), pageUtils.getPageSize(), pageUtils.getTotal(), voList);
    }


    /**
     * 新建部门
     *
     * @param essayDTO 部门输入对象
     * @return 部门编码
     */
    @Override
    public Integer addEssay(EssayDTO essayDTO) {
        Token token = ThreadContextHolder.getToken();
        // 校验输入数据正确性
        EssayUtils.validateEssay(essayDTO);
        // 创建实体对象，用以保存到数据库
        Essay essay = new Essay();
        // 将输入的字段全部复制到实体对象中
        BeanUtils.copyProperties(essayDTO, essay);
        essay.setCreatedAt(new Date());
        essay.setUpdatedAt(new Date());
        essay.setCreatedBy(token.getUserId());
        essay.setUpdatedBy(token.getUserId());
        // 调用DAO方法保存到数据库表
        essayMapper.insert(essay);

        // 新建elasticsearch索引
        List<BulkOperation> bulkList = new ArrayList<>();
        bulkList.add(ElasticsearchUtils.addBulkBook(essay.getId(), essay.getContent()));
        ElasticsearchUtils.bulkAddDocument(elasticsearchClient, bulkList);
        return essay.getId();
    }

    /**
     * 更新部门数据
     *
     * @param essayDTO 部门输入对象
     * @return 部门编码
     */
    @Override
    public Integer updateEssay(EssayDTO essayDTO) {
        // 校验输入数据正确性
        Token token = ThreadContextHolder.getToken();
        EssayUtils.validateEssay(essayDTO);
        Assert.notNull(essayDTO.getId(), "文章id不能为空");
        Essay essay = essayMapper.selectByPrimaryKey(essayDTO.getId());
        Assert.notNull(essay, "没有找到文章，Id为：" + essayDTO.getId());

        BeanUtils.copyProperties(essayDTO, essay);
        essay.setUpdatedBy(token.getUserId());
        essay.setUpdatedAt(new Date());
        essayMapper.updateByPrimaryKey(essay);
        ElasticsearchUtils.updateDocument(elasticsearchClient, essay.getId(), essay.getContent());
        return essay.getId();
    }

    /**
     * 根据编码列表，批量删除部门
     *
     * @param ids 编码列表
     */
    @Override
    public void deleteByCodes(List<Integer> ids) {
        Assert.notEmpty(ids, "文章id列表不能为空");
        essayMapper.deleteByCodes(ids);
        for (Integer id : ids) {
            ElasticsearchUtils.deleteDocument(elasticsearchClient, id);
        }
    }

    @Override
    public Page<LuceneResultBookVO> search(EssayFullSearchQueryDTO queryDTO) {  // 声明一个public方法，参数是EssayFullSearchQueryDTO类型，返回Page<LuceneResultBookVO>类型的结果
        Page<LuceneResultBookVO> result = ElasticsearchUtils.query(elasticsearchClient, queryDTO);  // 调用ElasticsearchUtils类的query方法，将elasticsearchClient和queryDTO作为参数传递给它，结果存储在result中，result是Page<LuceneResultBookVO>类型的对象。
        Set<Integer> ids = result.getList().stream().map(LuceneResultBookVO::getBookId).collect(Collectors.toSet());  // 从result中获取List，并将其转化为Stream，通过map操作获取每个LuceneResultBookVO对象的bookId属性，并使用collect方法将它们转化为Set<Integer>类型，存储在ids中。
        List<Essay> list = new ArrayList<>();  // 创建一个新的ArrayList<Essay>对象list
        if(!CollectionUtils.isEmpty(ids)) {  // 如果ids不为空
            list = essayMapper.listByIds(new ArrayList<>(ids));  // 将ids转换为ArrayList类型，调用essayMapper的listByIds方法，将结果存储在list中
        }
        Map<Integer, Essay> map = list.stream().collect(Collectors.toMap(Essay::getId, Function.identity()));  // 将list转换为Stream，使用toMap操作将每个Essay对象的id属性作为key，对象本身作为value存储在Map<Integer, Essay>类型的对象map中。
        result.getList().forEach(item-> {  // 获取result中的List，遍历每个元素，使用Lambda表达式操作
            Essay essay = map.get(item.getBookId());  // 从map中获取与当前元素的bookId属性对应的Essay对象，存储在essay中。
            if(essay!=null){  // 如果essay不为null
                item.setTitle(essay.getTitle());  // 将essay的title属性设置为当前元素的title属性
            }
        });
        return result;  // 返回result
    }
}